﻿import React, {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from 'react';
import * as SecureStore from 'expo-secure-store';

import { API_BASE_URL, resolveAssetUrl } from '@/constants/config';

type UserChannel = 'customer' | 'merchant';

export interface AuthUser {
  id: number;
  username: string;
  displayName?: string | null;
  email?: string | null;
  avatarUrl?: string | null;
  phone: string;
  channel: UserChannel;
  role?: 'customer' | 'merchant';
}

interface SignInPayload {
  credential: string;
  password: string;
  channel: UserChannel;
}

interface SignUpPayload {
  phone: string;
  password: string;
  channel: Extract<UserChannel, 'customer'>;
}

interface AuthContextValue {
  user: AuthUser | null;
  token: string | null;
  merchantToken: string | null;
  isAuthenticated: boolean;
  isRestoring: boolean;
  viewMode: UserChannel;
  setViewMode: (mode: UserChannel) => void;
  signIn: (payload: SignInPayload) => Promise<AuthUser>;
  signUp: (payload: SignUpPayload) => Promise<AuthUser>;
  signOut: () => void;
  updateProfile: (payload: { displayName?: string; avatarUrl?: string | null }) => Promise<AuthUser>;
}

interface ApiEnvelope<T> {
  data?: T;
  error?: string;
  message?: string;
  meta?: unknown;
}

interface BackendUser {
  id: number;
  username: string;
  display_name?: string | null;
  email?: string | null;
  avatar_url?: string | null;
  role?: 'customer' | 'merchant';
}

interface LoginResponse {
  token: string;
  user: BackendUser;
}

const AuthContext = createContext<AuthContextValue | undefined>(undefined);

const TOKEN_STORAGE_KEY = 'photosalesplus_auth_token';
const USER_STORAGE_KEY = 'photosalesplus_auth_user';
const VIEW_MODE_STORAGE_KEY = 'photosalesplus_view_mode';
const MERCHANT_TOKEN_STORAGE_KEY = 'photosalesplus_merchant_token';

const canUseBrowserStorage =
  typeof window !== 'undefined' && typeof window.localStorage !== 'undefined';

async function setStoredValue(key: string, value: string) {
  try {
    await SecureStore.setItemAsync(key, value);
    return;
  } catch (err) {
    if (canUseBrowserStorage) {
      window.localStorage.setItem(key, value);
      return;
    }
    console.warn('Failed to persist auth data', err);
  }
}

async function getStoredValue(key: string) {
  try {
    const result = await SecureStore.getItemAsync(key);
    if (result !== null) {
      return result;
    }
  } catch (err) {
    if (!canUseBrowserStorage) {
      console.warn('Failed to load auth data', err);
    }
  }

  if (canUseBrowserStorage) {
    return window.localStorage.getItem(key);
  }

  return null;
}

async function deleteStoredValue(key: string) {
  try {
    await SecureStore.deleteItemAsync(key);
  } catch (err) {
    if (!canUseBrowserStorage) {
      console.warn('Failed to clear auth data', err);
    }
  } finally {
    if (canUseBrowserStorage) {
      window.localStorage.removeItem(key);
    }
  }
}

const formatPhone = (value: string) => value.replace(/\D/g, '');

const resolveErrorMessage = (payload: ApiEnvelope<unknown> | null, fallback: string) => {
  if (!payload) {
    return fallback;
  }

  if (typeof payload.error === 'string' && payload.error.trim().length > 0) {
    return payload.error.trim();
  }

  if (typeof payload.message === 'string' && payload.message.trim().length > 0) {
    return payload.message.trim();
  }

  return fallback;
};

const NETWORK_ERROR_MESSAGE = '无法连接服务器，请稍后重试。';
const LOGIN_FAILED_MESSAGE = '登录失败，请稍后重试。';
const REGISTER_FAILED_MESSAGE = '注册失败，请稍后重试。';
const INVALID_PHONE_MESSAGE = '请输入有效的手机号。';
const INVALID_MERCHANT_USERNAME = '请输入商户用户名。';
const INVALID_PASSWORD_MESSAGE = '请输入密码。';
const PROFILE_UPDATE_FAILED_MESSAGE = '更新个人资料失败，请稍后重试。';

const normalizeAvatarUrl = (value?: string | null) => {
  if (!value) {
    return '';
  }
  const trimmed = value.trim();
  if (!trimmed) {
    return '';
  }
  return resolveAssetUrl(trimmed);
};

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>(null);
  const [token, setToken] = useState<string | null>(null);
  const [merchantToken, setMerchantToken] = useState<string | null>(null);
  const [isRestoring, setRestoring] = useState(true);
  const [viewMode, setViewModeState] = useState<UserChannel>('customer');

  const persistSession = useCallback(async (nextToken: string, nextUser: AuthUser) => {
    try {
      await Promise.all([
        setStoredValue(TOKEN_STORAGE_KEY, nextToken),
        setStoredValue(USER_STORAGE_KEY, JSON.stringify(nextUser)),
      ]);
    } catch (err) {
      console.warn('Failed to persist session', err);
    }
  }, []);

  const clearSession = useCallback(async () => {
    try {
      await Promise.all([
        deleteStoredValue(TOKEN_STORAGE_KEY),
        deleteStoredValue(USER_STORAGE_KEY),
      ]);
    } catch (err) {
      console.warn('Failed to clear session', err);
    }
  }, []);

  useEffect(() => {
    let isMounted = true;

    (async () => {
      try {
        const [storedToken, storedMerchantToken, storedUserJson, storedViewMode] = await Promise.all([
          getStoredValue(TOKEN_STORAGE_KEY),
          getStoredValue(MERCHANT_TOKEN_STORAGE_KEY),
          getStoredValue(USER_STORAGE_KEY),
          getStoredValue(VIEW_MODE_STORAGE_KEY),
        ]);

        if (!isMounted) {
          return;
        }

        if ((storedToken || storedMerchantToken) && storedUserJson) {
          try {
            const parsedUser = JSON.parse(storedUserJson) as AuthUser;
            const normalizedUser: AuthUser = {
              ...parsedUser,
              avatarUrl: normalizeAvatarUrl(
                (parsedUser as any).avatarUrl ?? (parsedUser as any).avatar_url ?? '',
              ),
            };
            setToken(storedToken);
            setMerchantToken(storedMerchantToken);
            setUser(normalizedUser);
            if (storedViewMode === 'customer' || storedViewMode === 'merchant') {
              setViewModeState(storedViewMode);
            } else {
              setViewModeState(normalizedUser.channel);
            }
          } catch (err) {
            console.warn('Failed to parse stored user', err);
            await clearSession();
          }
        } else if (storedViewMode === 'customer' || storedViewMode === 'merchant') {
          setViewModeState(storedViewMode);
        }
      } catch (err) {
        console.warn('Failed to restore session', err);
      } finally {
        if (isMounted) {
          setRestoring(false);
        }
      }
    })();

    return () => {
      isMounted = false;
    };
  }, [clearSession]);

  const authenticate = useCallback(
    async (username: string, password: string, channel: UserChannel) => {
      const loginUrl = channel === 'merchant'
        ? `${API_BASE_URL}/merchant/login`
        : `${API_BASE_URL}/auth/login`;
      let envelope: ApiEnvelope<LoginResponse> | null = null;

      let response: Response;
      try {
        response = await fetch(loginUrl, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ username, password }),
        });
      } catch {
        throw new Error(NETWORK_ERROR_MESSAGE);
      }

      try {
        envelope = (await response.json()) as ApiEnvelope<LoginResponse>;
      } catch {
        envelope = null;
      }

      if (!response.ok) {
        throw new Error(resolveErrorMessage(envelope, LOGIN_FAILED_MESSAGE));
      }

      const loginData = envelope?.data as any;
      if (!loginData || typeof loginData.token !== 'string' || (!loginData.user && !loginData.merchant)) {
        throw new Error('登录返回数据不完整，请稍后重试。');
      }

      const backendUser = (loginData.user ?? loginData.merchant) as BackendUser;
      const nextUser: AuthUser = {
        id: backendUser.id,
        username: backendUser.username,
        displayName: backendUser.display_name ?? '',
        email: backendUser.email ?? '',
        avatarUrl: normalizeAvatarUrl(backendUser.avatar_url),
        phone: username,
        channel,
        role: (backendUser as any).role ?? (channel as 'customer' | 'merchant'),
      };

      if (channel === 'merchant') {
        setMerchantToken(loginData.token);
        setUser(nextUser);
        setViewModeState('merchant');
        setStoredValue(VIEW_MODE_STORAGE_KEY, 'merchant').catch((err) =>
          console.warn('Persist view mode failed', err),
        );
        await setStoredValue(MERCHANT_TOKEN_STORAGE_KEY, loginData.token);
        await setStoredValue(USER_STORAGE_KEY, JSON.stringify(nextUser));
      } else {
        setToken(loginData.token);
        setUser(nextUser);
        setViewModeState('customer');
        setStoredValue(VIEW_MODE_STORAGE_KEY, 'customer').catch((err) =>
          console.warn('Persist view mode failed', err),
        );
        persistSession(loginData.token, nextUser).catch((err) =>
          console.warn('Persist session failed', err),
        );
      }
      return nextUser;
    },
    [persistSession],
  );

  const signIn = useCallback(
    async ({ credential, password, channel }: SignInPayload) => {
      const normalizedCredential =
        channel === 'customer' ? formatPhone(credential) : credential.trim();

      if (channel === 'customer' && normalizedCredential.length !== 11) {
        throw new Error(INVALID_PHONE_MESSAGE);
      }

      if (channel === 'merchant' && normalizedCredential.length === 0) {
        throw new Error(INVALID_MERCHANT_USERNAME);
      }

      if (!password.trim()) {
        throw new Error(INVALID_PASSWORD_MESSAGE);
      }

      return authenticate(normalizedCredential, password, channel);
    },
    [authenticate],
  );

  const signUp = useCallback(
    async ({ phone, password, channel }: SignUpPayload) => {
      const normalizedPhone = formatPhone(phone);
      if (!normalizedPhone) {
        throw new Error(INVALID_PHONE_MESSAGE);
      }

      const registerUrl = `${API_BASE_URL}/auth/register`;
      let envelope: ApiEnvelope<BackendUser> | null = null;

      let response: Response;
      try {
        response = await fetch(registerUrl, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            username: normalizedPhone,
            password,
          }),
        });
      } catch {
        throw new Error(NETWORK_ERROR_MESSAGE);
      }

      try {
        envelope = (await response.json()) as ApiEnvelope<BackendUser>;
      } catch {
        envelope = null;
      }

      if (!response.ok) {
        throw new Error(resolveErrorMessage(envelope, REGISTER_FAILED_MESSAGE));
      }

      return authenticate(normalizedPhone, password, channel);
    },
    [authenticate],
  );

  const signOut = useCallback(() => {
    setToken(null);
    setMerchantToken(null);
    setUser(null);
    Promise.all([
      deleteStoredValue(TOKEN_STORAGE_KEY),
      deleteStoredValue(MERCHANT_TOKEN_STORAGE_KEY),
      deleteStoredValue(USER_STORAGE_KEY),
    ]).catch((err) => console.warn('Clear session failed', err));
  }, []);

  const updateProfile = useCallback(
    async ({ displayName, avatarUrl }: { displayName?: string; avatarUrl?: string | null }) => {
      if (!user) {
        throw new Error('未登录，请先登录后再试。');
      }

      const authToken = user.channel === 'merchant' ? merchantToken : token;
      if (!authToken) {
        throw new Error('登录状态已过期，请重新登录后再试。');
      }

      const payload: Record<string, unknown> = {};
      if (displayName !== undefined) {
        payload.display_name = displayName;
      }
      if (avatarUrl !== undefined) {
        payload.avatar_url = avatarUrl;
      }

      if (Object.keys(payload).length === 0) {
        return user;
      }

      const updateUrl = `${API_BASE_URL}/profile`;
      let envelope: ApiEnvelope<BackendUser> | null = null;

      let response: Response;
      try {
        response = await fetch(updateUrl, {
          method: 'PATCH',
          headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${authToken}`,
          },
          body: JSON.stringify(payload),
        });
      } catch {
        throw new Error(NETWORK_ERROR_MESSAGE);
      }

      try {
        envelope = (await response.json()) as ApiEnvelope<BackendUser>;
      } catch {
        envelope = null;
      }

      if (!response.ok) {
        throw new Error(resolveErrorMessage(envelope, PROFILE_UPDATE_FAILED_MESSAGE));
      }

      const backendUser = envelope?.data;
      if (!backendUser) {
        throw new Error('更新个人资料失败，请稍后重试。');
      }

      const nextUser: AuthUser = {
        ...user,
        displayName: backendUser.display_name ?? user.displayName ?? '',
        email: backendUser.email ?? user.email ?? '',
        avatarUrl: normalizeAvatarUrl(backendUser.avatar_url) || user.avatarUrl || '',
      };
      setUser(nextUser);

      if (user.channel === 'merchant') {
        await setStoredValue(USER_STORAGE_KEY, JSON.stringify(nextUser));
      } else if (token) {
        persistSession(token, nextUser).catch((err) =>
          console.warn('Persist session failed', err),
        );
      }

      return nextUser;
    },
    [merchantToken, persistSession, token, user],
  );

  const value = useMemo<AuthContextValue>(
    () => ({
      user,
      token,
      merchantToken,
      isAuthenticated: !!token || !!merchantToken,
      isRestoring,
      viewMode,
      setViewMode: (mode: UserChannel) => {
        setViewModeState(mode);
        setStoredValue(VIEW_MODE_STORAGE_KEY, mode).catch((err) =>
          console.warn('Persist view mode failed', err),
        );
      },
      signIn,
      signUp,
      signOut,
      updateProfile,
    }),
    [isRestoring, signIn, signOut, signUp, token, merchantToken, updateProfile, user, viewMode],
  );

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  const context = useContext(AuthContext);

  if (!context) {
    throw new Error('useAuth must be used within AuthProvider.');
  }

  return context;
}



